原始题目:剑指 Offer 36. 二叉搜索树与双向链表 (opens new window)

解题思路:

因为是二叉搜索树,和排序相关的首先想到的是中序遍历,因此此题应该建立在中序遍历的基础上,对树的结构进行改造。

我们使用两个指针来辅助完成改造,分别是 prepre 和$ head$。

  • prepre 指向的是当前操作节点的前驱节点, 在未经过最左节点时,$pre $ 都是处于 $null $ 的状态。之后,prepre 都会指向最近经过的节点
  • headhead 是整个链表的表头,一开始也为 nullnullheadhead 应当指向二叉搜索树的最左节点,因为它是最小的。

中序遍历函数

**递归参数:**当前节点 rootroot

终止条件:rootrootnullnull 时,表示整棵树已经遍历完毕,返回。

递归工作:

  • 遍历 $root $ 的左子树。
  • 如果 prepre 不为 nullnull,则将 pre.leftpre.left 指向 rootrootroot.rightroot.right 指向 prepre,前后驱节点互指。
  • 如果 headheadnullnull,则将 headhead 指向 rootroot
  • prepre 指向 rootroot
  • 遍历 rootroot 的右子树。

经过中序遍历函数之后,prepre 会指向最右子节点,headhead 指向最左子节点。prepre 指向链表表尾,headhead 指向链表表头。

转换函数

  • 调用中序遍历函数。
  • prepre 和 $head $ 互指,即头尾节点互指,pre.right=headpre.right = headhead.left=prehead.left = pre
  • 返回 headhead

代码:

public Node treeToDoublyList(Node root) {
    if(root == null){
        return null;
    }
    inorder(root);
    // 头尾节点互指
    pre.right = head;
    head.left = pre;
    return head;
}

private void inorder(Node root) {
    if (root == null) {
        return;
    }
    inorder(root.left);
    if (pre != null) {
        pre.right = root;
        root.left = pre;
    }
    if (head == null) {
        // head 指向最左节点
        head = root;
    }
    pre = root;
    inorder(root.right);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

复杂度分析

  • 时间复杂度O(N)O(N)NN 为二叉搜索树的节点个数。需要对每个节点进行前后驱节点互指,互指使用 O(1)O(1) 时间。
  • 时间复杂度O(N)O(N)NN 为二叉搜索树的节点个数。最差情况下,树退化成链表,进行 NN 次递归,需要 O(N)O(N) 的栈空间。
上次更新: 2023/10/15